# -*- coding: utf-8 -*-
# Author   : ZhangQing
# Time     : 2025-07-15 23:17
# File     : ib_adapter.py
# Project  : dynamic-portfolio-optimizer
# Desc     :
# src/data/adapters/ib_adapter.py
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.order import Order
import threading
import time
from typing import Dict, List, Any
import pandas as pd
from datetime import datetime
from .base_adapter import BaseDataAdapter


class IBWrapper(EWrapper):
    """IB API包装器"""

    def __init__(self):
        EWrapper.__init__(self)
        self.historical_data = []
        self.contract_details = {}
        self.market_data = {}
        self.data_ready = False

    def historicalData(self, reqId, bar):
        """历史数据回调"""
        self.historical_data.append({
            'timestamp': datetime.strptime(bar.date, '%Y%m%d %H:%M:%S'),
            'open': bar.open,
            'high': bar.high,
            'low': bar.low,
            'close': bar.close,
            'volume': bar.volume
        })

    def historicalDataEnd(self, reqId, start, end):
        """历史数据结束回调"""
        self.data_ready = True

    def contractDetails(self, reqId, contractDetails):
        """合约详情回调"""
        self.contract_details[reqId] = contractDetails

    def tickPrice(self, reqId, tickType, price, attrib):
        """实时价格回调"""
        if reqId not in self.market_data:
            self.market_data[reqId] = {}
        self.market_data[reqId][tickType] = price


class IBAdapter(BaseDataAdapter):
    """Interactive Brokers数据适配器"""

    def __init__(self, config):
        super().__init__(config)
        self.wrapper = IBWrapper()
        self.client = EClient(self.wrapper)
        self.connected = False
        self.next_valid_id = 1000

    def connect(self, host='127.0.0.1', port=7497, clientId=1):
        """连接到TWS或IB Gateway"""
        try:
            self.client.connect(host, port, clientId)

            # 启动消息循环
            api_thread = threading.Thread(target=self.client.run)
            api_thread.daemon = True
            api_thread.start()

            # 等待连接建立
            time.sleep(2)
            self.connected = True

            self.logger.info("已连接到Interactive Brokers")

        except Exception as e:
            self.logger.error(f"连接IB失败: {e}")
            self.connected = False

    def disconnect(self):
        """断开连接"""
        if self.connected:
            self.client.disconnect()
            self.connected = False

    def create_contract(self, symbol: str, sec_type: str = 'STK',
                        exchange: str = 'SMART', currency: str = 'USD') -> Contract:
        """创建合约对象"""
        contract = Contract()
        contract.symbol = symbol
        contract.secType = sec_type
        contract.exchange = exchange
        contract.currency = currency
        return contract

    def get_stock_data(self, symbol: str, start_date: datetime,
                       end_date: datetime, interval: str = '1d') -> pd.DataFrame:
        """获取股票数据"""
        if not self.connected:
            self.connect()

        try:
            # 创建合约
            contract = self.create_contract(symbol)

            # 转换时间间隔
            bar_size_map = {
                '1m': '1 min',
                '5m': '5 mins',
                '15m': '15 mins',
                '30m': '30 mins',
                '1h': '1 hour',
                '1d': '1 day'
            }

            bar_size = bar_size_map.get(interval, '1 day')

            # 计算持续时间
            duration = (end_date - start_date).days
            duration_str = f"{duration} D"

            # 清空历史数据
            self.wrapper.historical_data = []
            self.wrapper.data_ready = False

            # 请求历史数据
            req_id = self.next_valid_id
            self.next_valid_id += 1

            self.client.reqHistoricalData(
                req_id, contract, end_date.strftime('%Y%m%d %H:%M:%S'),
                duration_str, bar_size, 'TRADES', 1, 1, False, []
            )

            # 等待数据
            timeout = 30
            start_time = time.time()
            while not self.wrapper.data_ready and (time.time() - start_time) < timeout:
                time.sleep(0.1)

            if not self.wrapper.data_ready:
                self.logger.warning(f"IB: 获取{symbol}数据超时")
                return pd.DataFrame()

            # 转换为DataFrame
            df = pd.DataFrame(self.wrapper.historical_data)
            if not df.empty:
                df.set_index('timestamp', inplace=True)
                df['source'] = 'ib_api'
                df['symbol'] = symbol

                return self.standardize_data(df)

            return pd.DataFrame()

        except Exception as e:
            self.logger.error(f"IB获取股票数据失败: {e}")
            return pd.DataFrame()

    def get_options_data(self, symbol: str, expiration_date: datetime) -> pd.DataFrame:
        """获取期权数据"""
        if not self.connected:
            self.connect()

        try:
            # 创建期权合约
            contract = Contract()
            contract.symbol = symbol
            contract.secType = 'OPT'
            contract.exchange = 'SMART'
            contract.currency = 'USD'
            contract.lastTradeDateOrContractMonth = expiration_date.strftime('%Y%m%d')

            # 请求期权链（这里简化处理）
            # 实际使用中需要遍历不同的执行价格
            req_id = self.next_valid_id
            self.next_valid_id += 1

            self.client.reqContractDetails(req_id, contract)

            # 等待合约详情
            time.sleep(5)

            # 处理期权数据
            options_data = []
            for contract_detail in self.wrapper.contract_details.values():
                options_data.append({
                    'symbol': symbol,
                    'contract_symbol': contract_detail.contract.localSymbol,
                    'strike': contract_detail.contract.strike,
                    'expiration': expiration_date,
                    'type': 'call' if contract_detail.contract.right == 'C' else 'put',
                    'exchange': contract_detail.contract.exchange,
                    'source': 'ib_api'
                })

            return pd.DataFrame(options_data)

        except Exception as e:
            self.logger.error(f"IB获取期权数据失败: {e}")
            return pd.DataFrame()

    def get_fundamentals(self, symbol: str) -> Dict[str, Any]:
        """获取基本面数据"""
        if not self.connected:
            self.connect()

        try:
            # IB的基本面数据需要特殊处理
            contract = self.create_contract(symbol)

            # 请求基本面数据
            req_id = self.next_valid_id
            self.next_valid_id += 1

            self.client.reqFundamentalData(req_id, contract, 'ReportSnapshot', [])

            # 等待数据
            time.sleep(5)

            # 这里简化处理，实际需要解析XML响应
            fundamentals = {
                'symbol': symbol,
                'source': 'ib_api'
            }

            return fundamentals

        except Exception as e:
            self.logger.error(f"IB获取基本面数据失败: {e}")
            return {}

    def get_news(self, symbol: str, limit: int = 10) -> List[Dict[str, Any]]:
        """获取新闻数据"""
        if not self.connected:
            self.connect()

        try:
            # IB新闻API
            contract = self.create_contract(symbol)

            # 请求新闻
            req_id = self.next_valid_id
            self.next_valid_id += 1

            self.client.reqNewsArticle(req_id, "BRFG", "BRFG$04369d56", [])

            # 等待新闻数据
            time.sleep(5)

            # 这里需要实现新闻数据的处理
            news_data = []

            return news_data

        except Exception as e:
            self.logger.error(f"IB获取新闻数据失败: {e}")
            return []
